package demo01

import (
	"fmt"
	"sync"
	"sync/atomic"
)

/*
5. sync/Atomic
除了前面介绍的锁mutex以外，还有一种解决并发安全的策略，就是原子操作。所谓原子操作就是这一系列的操作在cpu上执行是一个不可分割的整体，显然要么全部执行，要么全部不执行，不会受到其他操作的影响，也就不会存在并发问题。

5.1 atomic和mutex的区别
使用方式： 通常mutex用于保护一段执行逻辑，而atomic主要是对变量进行操作
底层实现：mutex由操作系统调度器实现，而atomic操作有底层硬件指令支持，保证在cpu上执行不中断。所以atomic的性能也能随cpu的个数增加线性提升
atomic提供的方法：
func AddT(addr *T, delta T)(new T)
func StoreT(addr *T, val T)
func LoadT(addr *T) (val T)
func SwapT(addr *T, new T) (old T)
func CompareAndSwapT(addr *T, old, new T) (swapped bool)
T的类型是int32、int64、uint32、uint64和uintptr中的任意一种
这里就不一一演示各个方法了，以AddT方法为例简单看一个例子
*/

func AtomicDemo() {
	var sum int32 = 0
	var wg sync.WaitGroup
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			atomic.AddInt32(&sum, 1)
		}()
	}
	wg.Wait()
	fmt.Printf("sum is %d\n", sum)
}

/*
5.2 atomic.value
上面展示的AddT，StoreT等方法都是针对的基本数据类型做的操作，假设想对多个变量进行同步保护，即假设想对一个struct这样的复合类型用原子操作，也是支持的吗？
也可以做支持，go语言里的atomic.value支持任意一种接口类型进行原子操作，且提供了Load、Store、Swap和CompareAndSwap四种方法：
Load：func (v *Value) Load() (val any)，从value读出数据
Store：func (v *Value) Store(val any)，向value写入数据
Swap：func (v *Value) Swap(new any) (old any)，用new交换value中存储的数据，返回value原来存储的旧数据
CompareAndSwap：func (v *Value) CompareAndSwap(old, new any) (swapped bool)，比较value中存储的数据和old是否相同，相同的话，将value中的数据替换为new
代码示例：
*/

type Student struct {
	Name string
	Age  int
}

func AtomicValueDemo() {
	st1 := Student{
		Name: "zhangsan",
		Age:  99,
	}
	st2 := Student{
		Name: "lisi",
		Age:  98,
	}
	st3 := Student{
		Name: "wangwu",
		Age:  97,
	}
	var v atomic.Value
	v.Store(st1)
	fmt.Println(v.Load().(Student))

	old := v.Swap(st2)
	fmt.Printf("after swap: v=%v\n", v.Load().(Student))
	fmt.Printf("after swap: old=%v\n", old)

	// v中存储的和st1不相同，交换失败
	swapped := v.CompareAndSwap(st1, st3)
	fmt.Println("compare st1 and v\n", swapped, v)

	swapped = v.CompareAndSwap(st2, st3) // v中存储的和st2相同，交换成功，v中变为st3
	fmt.Println("compare st2 and v\n", swapped, v)
}
